package com.hmdp.utils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.hmdp.dto.UserDTO;
import com.hmdp.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.hmdp.utils.RedisConstants.LOGIN_USER_KEY;
import static com.hmdp.utils.RedisConstants.LOGIN_USER_TTL;

@Slf4j
public class LoginInterCeptor implements HandlerInterceptor {

    StringRedisTemplate stringRedisTemplate;

    public LoginInterCeptor(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//        1.获取请求头中的token
        String token = request.getHeader("Authorization");
//        判断
        if (StrUtil.isBlank(token))
        {
            response.setStatus(401);
            return false;
        }
//        2.基于token获取redis中的用户
        String key = LOGIN_USER_KEY+token;
        Map<Object, Object> usermap = stringRedisTemplate.opsForHash().entries(key);
        String requestURI = request.getRequestURI();
        log.info("判断是否进行拦截的路径为{}"+requestURI);
//        3.判断redis中的对象是否存在
        if (usermap==null)
        {
            log.info("当前还未登录");
            response.sendRedirect(request.getContextPath());
            log.info("拦截到请求{}"+request.getRequestURI());
            return false;
        }
//        4.此时，获取到的对象是map集合类型的，需要转换为userDTO类型的保存在threadlocal中
        /**
         * BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false)
         * 方法是将一个 Map 对象的值自动填充到一个 UserDTO 对象的对应属性中。
         * 该方法会自动进行类型转换和赋值操作，并返回填充后的 UserDTO 对象。
         */
        UserDTO userDTO = new UserDTO();
        BeanUtil.fillBeanWithMap(usermap,userDTO,false);
        UserHolder.saveUser(userDTO);
        log.info("用户已经登录，用户id为{}",userDTO.getId());
        /**
         * 在拦截器中如果会在多个请求之间共享同一个线程的情况下，
         * 如果直接使用 session 获取用户信息来进行操作，则会造成线程安全问题。
         * 为了避免这种情况，可以使用 ThreadLocal 来存储当前线程中的用户信息，
         * 这样不同的线程之间访问该变量时是互相独立的，不会出现线程安全问题。
         * 同时，使用 ThreadLocal 可以避免多个地方重复从 session 中获取用户信息，
         * 提高了代码的可读性和可维护性。每当有请求到来时，
         * 可以先从 ThreadLocal 中获取用户信息，如果获取不到再从 session 中获取
         * 这样就保证了用户数据只需要从 session 中取一次，避免了多次获取用户信息的性能消耗
         * 因此，将 session 中的用户信息保存在 ThreadLocal 中，
         * 可以避免线程安全问题，提高了代码的可读性和性能。
         */
//        5.刷新token有效期
        stringRedisTemplate.expire(key,LOGIN_USER_TTL, TimeUnit.MINUTES);
//        6.放行
        return true;
//
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 移除用户
        UserHolder.removeUser();

    }
}
